//
//  CLRequest.m
//  Charles
//
//  Created by Charles on 2018/6/6.
//  Copyright © 2018年 charles. All rights reserved.
//

#import "CLRequest.h"
#import "CLNetWorkConfig.h"

#ifndef NSFoundationVersionNumber_iOS_8_0
#define NSFoundationVersionNumber_With_QoS_Available 1140.11
#else
#define NSFoundationVersionNumber_With_QoS_Available NSFoundationVersionNumber_iOS_8_0
#endif

NSString *const CLRequestCacheErrorDomain = @"com.charles.request.caching";
static dispatch_queue_t clrequest_cache_queue() {
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_queue_attr_t attr = DISPATCH_QUEUE_SERIAL;
        if (NSFoundationVersionNumber >= NSFoundationVersionNumber_With_QoS_Available) {
            attr = dispatch_queue_attr_make_with_qos_class(attr,QOS_CLASS_BACKGROUND,0);
        }
        queue = dispatch_queue_create("com.charles.request.caching", attr);
    });
    return queue;
}

@interface CLCacheMetadata : NSObject<NSSecureCoding>

@property (nonatomic,assign) long long version;
@property (nonatomic,strong) NSString *sensitiveDataString;
@property (nonatomic,assign) NSStringEncoding stringEncoding;
@property (nonatomic,strong) NSDate *creationDate;
@property (nonatomic,strong) NSString *appVersionString;

@end

@implementation CLCacheMetadata

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:@(self.version) forKey:NSStringFromSelector(@selector(version))];
    [aCoder encodeObject:self.sensitiveDataString forKey:NSStringFromSelector(@selector(sensitiveDataString))];
    [aCoder encodeObject:@(self.stringEncoding) forKey:NSStringFromSelector(@selector(stringEncoding))];
    [aCoder encodeObject:self.creationDate forKey:NSStringFromSelector(@selector(creationDate))];
    [aCoder encodeObject:self.appVersionString forKey:NSStringFromSelector(@selector(appVersionString))];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        self.version = [[aDecoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(version))] integerValue];
        self.sensitiveDataString = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(sensitiveDataString))];
        self.creationDate = [aDecoder decodeObjectOfClass:[NSDate class] forKey:NSStringFromSelector(@selector(creationDate))];
        self.appVersionString = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(appVersionString))];
    }
    return self;
}

@end

@interface CLRequest()

@property (nonatomic, strong) NSData *cacheData;
@property (nonatomic, strong) NSString *cacheString;
@property (nonatomic, strong) id cacheJSON;
@property (nonatomic, strong) NSXMLParser *cacheXML;
@property (nonatomic, strong) CLCacheMetadata *cacheMetadata;
@property (nonatomic, assign) BOOL dataFromCache;

@end

@implementation CLRequest

- (void)start {
    if (self.ignoreCache) {
        [self startWithoutCache];
        return;
    }
    
    if (self.resumableDownloadPath) {
        [self startWithoutCache];
        return;
    }
    
    if (![self loadCacheWithError:nil]) {
        [self startWithoutCache];
        return;
    }
    
    _dataFromCache = YES;
    dispatch_async(dispatch_get_main_queue(), ^{
        CLRequest *strongSelf = self;
        [strongSelf.delegate requestFinished:self];
        if (strongSelf.successCompletionBlock) {
            strongSelf.successCompletionBlock(strongSelf);
        }
        [strongSelf clearCompletionBlock];
    });
}

- (void)startWithoutCache {
    [super start];
}

- (NSInteger)cacheTimeInSeconds {
    return -1;
}

- (long long)cacheVersion {
    return 0;
}

- (id)cacheSensitiveData {
    return nil;
}

- (BOOL)writeCacheAsynchronously {
    return YES;
}

- (BOOL)isDataFromCache {
    return _dataFromCache;
}

- (NSData *)responseData {
    if (_cacheData) {
        return _cacheData;
    }
    return [super responseData];
}

- (NSString *)responseString {
    if (_cacheString) {
        return _cacheString;
    }
    return [super responseString];
}

- (id)responseJSONObject {
    if (_cacheJSON) {
        return _cacheJSON;
    }
    return [super responseJSONObject];
}

- (id)responseObject {
    if (_cacheJSON) {
        return _cacheJSON;
    }
    if (_cacheXML) {
        return _cacheXML;
    }
    if (_cacheData) {
        return _cacheData;
    }
    return [super responseObject];
}

- (BOOL)loadCacheWithError:(NSError *__autoreleasing *)error {
    if ([self cacheTimeInSeconds] < 0) {
        if (error) {
            *error = [NSError errorWithDomain:CLRequestCacheErrorDomain code:CLRequestCacheErrorInvalidCacheTime userInfo:@{NSLocalizedDescriptionKey:@"Invalid cache time"}];
        }
        return NO;
    }
    if (![self loadCacheMetadata]) {
        if (error) {
            *error = [NSError errorWithDomain:CLRequestCacheErrorDomain code:CLRequestCacheErrorInvalidMetadata userInfo:@{NSLocalizedDescriptionKey:@"Invalid metadata. Cache may not exist"}];
        }
        return NO;
    }
    
    if (![self validateCacheWithError:error]) {
        if (error) {
            *error = [NSError errorWithDomain:CLRequestCacheErrorDomain code:CLRequestCacheErrorInvalidCacheData userInfo:@{NSLocalizedDescriptionKey:@"Invalid cache data"}];
        }
        return NO;
    }
    return YES;
}

- (void)saveResponseDataToCacheFile:(NSData *)data {
    
}

- (BOOL)loadCacheMetadata {
    return NO;
}

- (BOOL)loadCacheData {
    return NO;
}

- (BOOL)validateCacheWithError:(NSError * _Nullable __autoreleasing *)error {
    return NO;
}

@end
